import 'package:domain/model/projects_domain_model.dart';
import 'package:domain/usecases/projects_use_case.dart';
import 'package:feature_projects/projects/effect.dart';
import 'package:feature_projects/projects/event.dart';
import 'package:feature_projects/projects/state.dart';
import 'package:feature_projects/projects/view_model.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class MockUseCase extends Mock implements ProjectsUseCase {}

void main() {
  group('tests for projects view model', () {
    late MockUseCase useCase;
    late ProjectsViewModel viewModel;
    setUp(() {
      useCase = MockUseCase();
      viewModel = ProjectsViewModel(useCase: useCase);
    });

    tearDown(() {
      reset(useCase);
    });

    test('case1 - validate initial state', () {
      final state = viewModel.state;
      expect(state.isLoading, true);
      expect(state.list.isEmpty, true);
    });

    test('case2 - send init event', () async {
      final mockResult = ProjectsDomainModel(
        totalCount: 1,
        projects: [
          Project(
            title: 'title',
            desc: 'desc',
            link: 'link',
            niceDate: 'date',
            chapterId: 1,
          ),
        ],
      );
      when(
        () => useCase.fetchProjects(
          page: any(named: 'page'),
          chapterId: any(named: 'chapterId'),
        ),
      ).thenAnswer((_) async => mockResult);

      expectLater(
        viewModel.stream,
        emitsInOrder([
          isA<ProjectsState>()
              .having((s) => s.isLoading, 'isLoading', false)
              .having((s) => s.list.length, 'list.length', 1)
              .having((s) => s.list.first.chapterId, 'chapter id', 1)
              .having((s) => s.list.first.title, 'title', "title")
              .having((s) => s.list.first.desc, 'desc', "desc")
              .having((s) => s.list.first.link, 'link', "link")
              .having((s) => s.list.first.niceDate, 'niceDate', "date"),
        ]),
      );

      viewModel.sendEvent(InitEvent());
    });
    test('case3 - send refresh event', () async {
      final mockResult = ProjectsDomainModel(
        totalCount: 1,
        projects: [
          Project(
            title: 'title',
            desc: 'desc',
            link: 'link',
            niceDate: 'date',
            chapterId: 1,
          ),
        ],
      );
      when(
        () => useCase.fetchProjects(
          page: any(named: 'page'),
          chapterId: any(named: 'chapterId'),
        ),
      ).thenAnswer((_) async => mockResult);

      expectLater(viewModel.sideEffects, emits(isA<RefreshingFinished>()));

      expectLater(
        viewModel.stream,
        emitsInOrder([
          isA<ProjectsState>()
              .having((s) => s.list.length, 'list.length', 1)
              .having((s) => s.list.first.chapterId, 'chapter id', 1)
              .having((s) => s.list.first.title, 'title', "title")
              .having((s) => s.list.first.desc, 'desc', "desc")
              .having((s) => s.list.first.link, 'link', "link")
              .having((s) => s.list.first.niceDate, 'niceDate', "date"),
        ]),
      );

      viewModel.sendEvent(RefreshEvent());
    });

    test('case4 - send load more event', () async {
      final mockResult = ProjectsDomainModel(
        totalCount: 1,
        projects: [
          Project(
            title: 'title',
            desc: 'desc',
            link: 'link',
            niceDate: 'date',
            chapterId: 1,
          ),
        ],
      );
      when(
        () => useCase.fetchProjects(
          page: any(named: 'page'),
          chapterId: any(named: 'chapterId'),
        ),
      ).thenAnswer((_) async => mockResult);

      expectLater(viewModel.sideEffects, emits(isA<LoadingMoreFinished>()));

      expectLater(
        viewModel.stream,
        emitsInOrder([
          isA<ProjectsState>()
              .having((s) => s.list.length, 'list.length', 1)
              .having((s) => s.list.first.chapterId, 'chapter id', 1)
              .having((s) => s.list.first.title, 'title', "title")
              .having((s) => s.list.first.desc, 'desc', "desc")
              .having((s) => s.list.first.link, 'link', "link")
              .having((s) => s.list.first.niceDate, 'niceDate', "date"),
        ]),
      );
      viewModel.sendEvent(LoadMoreEvent());
    });
  });
}
